package com.zengdada.authority.utils;


import com.alibaba.fastjson.JSON;
import com.zengdada.authority.utils.transformdata.TransformData;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 12614
 * @contact 1261438511@qq.com
 * @date 2018/11/4 16:39
 * @description 常用工具
 */
@SuppressWarnings("unchecked")
public class CommonUtils {
    private static final Pattern LINE_PATTERN = Pattern.compile("_(\\w)");
    private static final Pattern HUMP_PATTERN = Pattern.compile("[A-Z]");
    private static final Pattern LOWER_CASE_AND_NUMBER = Pattern.compile("[a-z0-9]*");
    private static final Pattern CAPITAL_AND_NUMBER = Pattern.compile("[A-Z0-9]*");

    public final static Map<Class<?>, Class<?>> BASIC_PACKING_TYPES = new HashMap<Class<?>, Class<?>>() {{
        put(byte.class, Byte.class);
        put(short.class, Short.class);
        put(int.class, Integer.class);
        put(long.class, Long.class);
        put(char.class, Character.class);
        put(float.class, Float.class);
        put(double.class, Double.class);
        put(boolean.class, Boolean.class);
    }};
    public final static Map<Class<?>, Class<?>> PACKING_BASIC_TYPES = new HashMap<Class<?>, Class<?>>() {{
        put(Byte.class, byte.class);
        put(Short.class, short.class);
        put(Integer.class, int.class);
        put(Long.class, long.class);
        put(Character.class, char.class);
        put(Float.class, float.class);
        put(Double.class, double.class);
        put(Boolean.class, boolean.class);
    }};

    /**
     * 批量或者单个实体类转Map集合
     *
     * @param obj 批量 List<Object(bean)> || 单个 Object(bean) bean必须是 com.sybenya.** 下 的实体类
     * @param <T> extends List || Object (bean)
     * @return 批量 List<Map < String ,   Object>> || 单个 Map<String, Object>
     */
    public static <T> T batchOrSingleBeanToMap(Object obj, String beanPackPrefix) {
        Object result;
        if (obj == null) {
            return null;
        }
        if (obj instanceof List) {
            result = new ArrayList<Map<String, Object>>();
            for (Object o : (List<Object>) obj) {
                ((List<Map<String, Object>>) result).add(batchOrSingleBeanToMap(o, beanPackPrefix));
            }
        } else if (obj instanceof Map) {
            result = new HashMap<String, Object>(0);
            for (String key : ((Map<String, Object>) obj).keySet()) {
                ((Map<String, Object>) result).put(key, batchOrSingleBeanToMap(((Map<String, Object>) obj).get(key), beanPackPrefix));
            }
        } else if (obj.getClass().getName().startsWith(beanPackPrefix)) {
            result = new HashMap<String, Object>(0);
            try {
                Class<?> clazz = obj.getClass();
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
//                    String name = field.getName();
//                    PropertyDescriptor descriptor = null;
//                    try {
//                        descriptor = new PropertyDescriptor(name, clazz);
//                    } catch (IntrospectionException e) {
//                        continue;
//                    }
//                    Method method = descriptor.getReadMethod();
//                    Object value = method.invoke(obj);
                    field.setAccessible(true);
                    //获取属性
                    String name = field.getName();
                    //获取属性值
                    Object value = field.get(obj);
                    if (value != null && (value instanceof List || value instanceof Map || value.getClass().getName().startsWith(beanPackPrefix))) {
                        ((Map<String, Object>) result).put(name, batchOrSingleBeanToMap(value, beanPackPrefix));
                    } else {
                        if (!"class".equals(name)) {
                            ((Map<String, Object>) result).put(name, value);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            result = obj;
        }
        return (T) result;
    }

    /**
     * 转Map key 大写
     *
     * @param orgMap
     * @return
     */
    public static Map<String, Object> transformUpperCase(Map<String, Object> orgMap) {
        Map<String, Object> resultMap = new HashMap<>();
        if (orgMap == null || orgMap.isEmpty()) {
            return resultMap;
        }
        Set<String> keySet = orgMap.keySet();
        for (String key : keySet) {
            resultMap.put(key.toUpperCase(), orgMap.get(key));
        }
        return resultMap;
    }

    /**
     * 数据库字段转驼峰命名  会创建新对象
     *
     * @param obj List<Map<String, Object>> || Map<String, Object>
     * @param <T> extends List || Map
     * @return List<Map
     */
    public static <T> T depthTransformHumpNew(T obj) { // T list / Map
        Object result = null;
        if (obj instanceof List) {
            result = new ArrayList<Map<String, Object>>();
            for (Object o : (List) obj) {
                ((List) result).add(depthTransformHumpNew(o));
            }
        } else if (obj instanceof Map) {
            result = new HashMap<String, Object>();
            for (String key : ((Map<String, Object>) obj).keySet()) {
                ((Map) result).put(lineToHump(key), depthTransformHumpNew(((Map<String, Object>) obj).get(key)));
            }
        } else {
            result = obj;
        }
        return (T) result;
    }

    /**
     * 数据库字段转驼峰命名 不修改原对象 （只保留第一级） 用在分页插件
     *
     * @param obj List<Map<String, Object>> || Map<String, Object>
     * @param <T> extends List || Map
     */
    public static <T> void depthTransformHump(T obj) { // T list / Map
        if (obj instanceof List) {
            for (Object o : (List) obj) {
                depthTransformHump(o);
            }
        } else if (obj instanceof Map) {
            Map<String, Object> result = new HashMap<>();
            for (String key : ((Map<String, Object>) obj).keySet()) {
                depthTransformHump(((Map) obj).get(key));
                result.put((key.contains("_") || !Pattern.compile("[a-z]").matcher(key).matches()) ? lineToHump(key) : key, ((Map) obj).get(key));
            }
            ((Map) obj).clear();
            ((Map) obj).putAll(result);
        }
    }

    public static String lineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = LINE_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    public static String humpToLine(String str) {
        if (str.contains("_") || LOWER_CASE_AND_NUMBER.matcher(str).matches() || CAPITAL_AND_NUMBER.matcher(str).matches()) {
            return str.toUpperCase();
        }
        Matcher matcher = HUMP_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString().toUpperCase();
    }

    /**
     * 把obj类中String类型的参数的null值或"NULL","null"  与 空字符串 相互转换
     *
     * @param obj 实体类
     * @param tag true 转为空字符串 false 转为 null
     * @return {@link T}
     * @author zsa
     * @date 2020/10/25 16:02
     */
    public static <T> T reflectObjectTransString(T obj, boolean tag) throws Exception {
        Class<?> classz = obj.getClass();
        Field[] fields = classz.getDeclaredFields();
        for (Field field : fields) {
            if (String.class == field.getType()) {
                // 属性参数值首字母转成大写
                char[] cs = (field.getName()).toCharArray();
                cs[0] -= 32;
                Method getMethod = classz.getMethod("get" + String.valueOf(cs));
                Method setMethod = classz.getMethod("set" + String.valueOf(cs), String.class);
                Object value = getMethod.invoke(obj);
                if (tag && (null == value || "NULL".equals(value) || "null".equals(value))) {
                    setMethod.invoke(obj, "");
                }
                if (!tag && ("".equals(value) || "NULL".equals(value) || "null".equals(value))) {
                    setMethod.invoke(obj, new Object[]{null});
                }
            }
        }
        return obj;
    }

    /**
     * 深度拷贝对象（可能出现对象被转为Map）
     *
     * @param src
     * @param <T>
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static <T> T deepCopyObject(T src) throws IOException, ClassNotFoundException {
        if (src == null) {
            return null;
        }
        String jsonString = JSON.toJSONString(src);
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(jsonString);
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        if (src instanceof List && ((List) src).size() > 0) {
            return (T) JSON.parseArray((String) in.readObject(), ((List) src).get(0).getClass());
        } else {
            return (T) JSON.parseObject((String) in.readObject(), src.getClass());
        }
    }

    /**
     * list 转树结构 没有父亲的 parentFieldName 必须为空
     *
     * @param data              数据集合
     * @param childrenFieldName 节点的标识字段名
     * @param parentFieldName   指向父节点的标识字段名
     * @param childrenName      树中孩子集合名
     * @return
     */
    public static List<Map<String, Object>> generateTreeMap(List<Map<String, Object>> data, String childrenFieldName, String parentFieldName, String childrenName) {
        List<Map<String, Object>> result = new ArrayList<>();
        for (Map<String, Object> treeNode : data) {
            if (treeNode.get(parentFieldName) == null || "".equals(treeNode.get(parentFieldName))) {
                result.add(treeNode);
            }
            for (Map<String, Object> it : data) {
                if (String.valueOf(treeNode.get(childrenFieldName)).equals(String.valueOf(it.get(parentFieldName)))) {
                    List<Map<String, Object>> children = (List<Map<String, Object>>) treeNode.computeIfAbsent(childrenName, k -> new ArrayList<Map<String, Object>>());
//                    if (treeNode.computeIfAbsent(childrenName, k -> null) == null)
//                        treeNode.put(childrenName, new ArrayList<Map<String, Object>>());
//                    ((List) treeNode.get(childrenName)).add(it);
                    children.add(it);
                }
            }
        }
        return result;
    }

    /**
     * list 转树结构 没有其他限制
     *
     * @param data 数据集合
     * @param childrenFieldName 节点的标识字段名
     * @param parentFieldName 指向父节点的标识字段名
     * @param childrenName  树中孩子集合名
     * @return {@link List< Map< String, Object>>}
     * @author zsa
     * @date 2019/10/25 16:10
     */
    public static List<Map<String, Object>> generateTreeMapCurrency(List<Map<String, Object>> data, String childrenFieldName, String parentFieldName, String childrenName) {
        List<Map<String, Object>> result = new ArrayList<>();
        Map<String, Map<String, Object>> temp = new HashMap<>();
        //将数组转为Object的形式，key为数组中的id
        for (Map<String, Object> item : data) {
            item.put("isLast", true);
            temp.put(String.valueOf(item.get(childrenFieldName)), item);
        }
        //遍历结果集
        for (Map<String, Object> item : data) {
//            item = temp.get(item.get(childrenFieldName));
            item.put("isFirst", true);
            //在temp中取出key为当前item的父节点
            Map<String, Object> hashVP = temp.get(String.valueOf(item.get(parentFieldName)));
            //如果有父节点，将她添加到父节点的孩子集合中
            if (hashVP != null) {
                //检查是否有child属性
                if (hashVP.get(childrenName) != null) {
                    ((List<Map<String, Object>>) hashVP.get(childrenName)).add(item);
                } else {
                    hashVP.put(childrenName, new ArrayList<Map<String, Object>>() {{
                        add(item);
                    }});
                    hashVP.put("isLast", false);
                }
                item.put("isFirst", false);
            } else {
                result.add(temp.get(String.valueOf(item.get(childrenFieldName)))); //父节点
            }
        }
        return result;
    }


    public static void main(String[] args) {
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>() {{
            add(new HashMap<String, Object>() {{
                put("id", "3");
                put("pid", "1");
                put("name", "北京");
            }});
            add(new HashMap<String, Object>() {{
                put("id", "100");
                put("name", "日国");
            }});
            add(new HashMap<String, Object>() {{
                put("id", "00");
                put("pid", "100");
                put("name", "日国0");
            }});
            add(new HashMap<String, Object>() {{
                put("id", "2");
                put("pid", "1");
                put("name", "重庆");
            }});

            add(new HashMap<String, Object>() {{
                put("id", "20");
                put("pid", "2");
                put("name", "合成");
            }});

            add(new HashMap<String, Object>() {{
                put("id", "31");
                put("pid", "3");
                put("name", "北京22");
            }});
            add(new HashMap<String, Object>() {{
                put("id", "300");
                put("pid", "30");
                put("name", "北京1111");
            }});
            add(new HashMap<String, Object>() {{
                put("id", "30");
                put("pid", "3");
                put("name", "北京11");
            }});
            add(new HashMap<String, Object>() {{
                put("id", "1");
                put("pid", "0");
                put("name", "中国");
            }});
            add(new HashMap<String, Object>() {{
                put("id", "21");
                put("pid", "2");
                put("name", "南岸");
            }});
        }};
        List<Map<String, Object>> list1 = generateTreeMap(list, "id", "pid", "chi");
        List<Map<String, Object>> list2 = generateTreeMapCurrency(list, "id", "pid", "chi");
    }

    /**
     * 更具指定 字段获及值 取枚举中定义的常量
     *
     * @param enumType 枚举类
     * @param values   比如获取字段name为year 并且 value 为 1 的常量 put("name","year");put("value",1);
     * @param <T>      枚举类型
     * @return 枚举常量
     * @throws Exception
     */
    public static <T extends Enum<T>> T enumValueOf(Class<T> enumType, Map<String, Object> values) throws Exception {
        Method valuesMethod = enumType.getMethod("values");
        Object invoke = valuesMethod.invoke(null);
        if (invoke != null) {
            T[] enumValues = (T[]) invoke; // 获取枚举定义的值
            Field[] fields = enumType.getDeclaredFields(); // 获取类字段
            List<Field> fieldList = new ArrayList<>();
            for (String fieldName : values.keySet()) {
                for (Field field : fields) {
                    if (field.getType() != enumType && field.getName().equals(fieldName)) {
                        fieldList.add(field);
                    }
                }
            }
            for (T value : enumValues) {
                int i = 0;
                for (Field field : fieldList) {
                    field.setAccessible(true);
                    Object o = field.get(value);
                    Object o1 = values.get(field.getName());
                    if ((o1.getClass().isPrimitive() && o1 != o) || (!o1.getClass().isPrimitive() && !o1.equals(o))){
//                    if ((o1 instanceof String && !o1.equals(o)) || (!(o1 instanceof String) && o1 != o))
                        break;
                    }
                    i++;
                }
                if (i == fieldList.size()) {
                    return value;
                }
            }
        }
        return null;
    }


    /**
     * 数据格式转换 不修改原对象 （只保留第一级） 用在分页插件
     *
     * @param obj   List<Map<String, Object>> || Map<String, Object>
     * @param modes 转换类
     * @param <T>   extends List || Map
     * @return 转换后的结果
     */
    public static <T> T depthTransformDataType(T obj, List<TransformData> modes) { // T list / Map
        if (modes == null || modes.size() == 0) {
            return obj;
        }
        if (obj instanceof List) {
            for (Object o : (List<Map<String, Object>>) obj) {
                depthTransformDataType(o, modes);
            }
        } else if (obj instanceof Map) {
            for (String key : ((Map<String, Object>) obj).keySet()) {
                Object val = ((Map<String, Object>) obj).get(key);
                if (val instanceof List || val instanceof Map) {
                    depthTransformDataType(val, modes);
                } else {
                    for (TransformData mode : modes) {
                        if (mode.equalType(val)) {
                            ((Map<String, Object>) obj).put(key, mode.transform(val));
                        }
                    }
                }
            }
        } else {
            throw new RuntimeException("需要转换的对象异常，必须满足  List<Map<String, Object>> 或者 Map<String, Object>");
        }
        return obj;
    }

    /**
     * 不定长日期格式字符串转日期
     *
     * @param str 目标字符串
     * @return 转换成功的日期
     * @throws ParseException
     */
    public static Date randomLengthStringToDate(String str) throws ParseException {
        String formatStr = "yyyy";
        str = str.trim();
        if (str.indexOf(" ") > 0) {
            if (str.indexOf("-") > 0) {
                formatStr = "yyyy-MM-dd HH:mm:ss";
            } else if (str.indexOf("/") > 0) {
                formatStr = "yyyy/MM/dd HH:mm:ss";
            } else {
                formatStr = "yyyyMMdd HH:mm:ss";
            }
        } else {
            if (str.indexOf("-") > 0) {
                formatStr = CommonUtils.zInStrAppearCount(str, "-") == 1 ? "yyyy-MM" : "yyyy-MM-dd";
            }
            if (str.indexOf("/") > 0) {
                formatStr = CommonUtils.zInStrAppearCount(str, "/") == 1 ? "yyyy/MM" : "yyyy/MM/dd";
            } else if (str.length() > 4) {
                formatStr = str.length() > 6 ? "yyyyMMdd" : "yyyyMM";
            }
        }
        return new SimpleDateFormat(formatStr, Locale.CHINA).parse(str);
    }

    /**
     * 统计 字符或者字符串 在字符串中出现次数
     *
     * @param str 字符串
     * @param z   字符或者字符串或者正则表达式
     * @return
     */
    public static int zInStrAppearCount(String str, String z) {
        //e表示需要匹配的数据，使用Pattern建立匹配模式
        Pattern p = Pattern.compile(z);
        //使用Matcher进行各种查找替换操作
        Matcher m = p.matcher(str);
        int i = 0;
        while (m.find()) {
            i++;
        }
        return i;
    }

    /**
     * // 通过指定无参方法名 获取 对象方法返回值
     *
     * @param o                对象
     * @param functionName     无参方法名
     * @param defaultVal       默认值 （不抛出异常 或者 方法返回值为空时 有效 ）
     * @param isThrowException 是否抛出异常
     * @return {@link T}
     * @author 12614
     * @date 2020-05-27 23:44
     */
    public static <T> T getObjectAllocateFunctionVal(Object o, String functionName, T defaultVal, boolean isThrowException) {
        T result = defaultVal;
        if (o == null || StringUtils.isEmpty(functionName)) {
            if (isThrowException) {
                throw new IllegalArgumentException("o 和 functionName 均不能为空");
            }
            return result;
        }
        try {
            Class<?> aClass = o.getClass();
            Method method = aClass.getMethod(functionName);
            Object invoke = method.invoke(o);
            if (invoke != null) {
                result = (T) invoke;
            }
        } catch (Exception e) {
            if (isThrowException) {
                throw new RuntimeException(e);
            }
        }
        return result;
    }

    public static <T> T getObjectAllocateFunctionVal(Object o, String functionName, T defaultVal) {
        return getObjectAllocateFunctionVal(o, functionName, defaultVal, false);
    }


    public static String firstCharUpperCase(String str) {
        char[] ch = str.toCharArray();
        if (ch[0] >= 'a' && ch[0] <= 'z') {
            ch[0] = (char) (ch[0] - 32);
        }
        return new String(ch);
    }


    /**
     * // 将src对象的属性 通过 dest 的set方法设置
     *
     * @param src         源
     * @param dest        目标
     * @param errContinue 设置值时错误是否继续
     * @author 12614
     * @date 2020-05-30 1:48
     */
    public static void copyObjectNotNullAttribute(Object src, Object dest, boolean errContinue) {
        if (src == null || dest == null) {
            return;
        }
        Class<?> srcClass = src.getClass();
        Class<?> destClass = dest.getClass();
        Field[] fields = srcClass.getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                //获取属性值
                Object value = field.get(src);
                if (value != null) {
                    Class<?> vClass = value.getClass();
                    // 拼接设置值的方法名
                    String setValMethodName = "set" + CommonUtils.firstCharUpperCase(field.getName());
                    try {
                        Method destSetValClassMethod = null;
                        try {
                            destSetValClassMethod = destClass.getMethod(setValMethodName, vClass);
                        } catch (NoSuchMethodException e) {
                            if (CommonUtils.PACKING_BASIC_TYPES.containsKey(vClass)) {
                                destSetValClassMethod = destClass.getMethod(setValMethodName, CommonUtils.PACKING_BASIC_TYPES.get(vClass));
                            } else if (CommonUtils.BASIC_PACKING_TYPES.containsKey(vClass)) {
                                destSetValClassMethod = destClass.getMethod(setValMethodName, CommonUtils.BASIC_PACKING_TYPES.get(vClass));
                            } else {
                                throw e;
                            }
                        }
                        destSetValClassMethod.invoke(dest, value);
                    } catch (NoSuchMethodException e) {
                        //
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        if (!errContinue) {
                            throw e;
                        }
                    }
                }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public static void copyObjectNotNullAttributeByGetMethod(Object src, Object dest, boolean errContinue) {
        if (src == null || dest == null) {
            return;
        }
        Class<?> srcClass = src.getClass();
        Class<?> destClass = dest.getClass();
        Method[] methods = srcClass.getDeclaredMethods();
        try {
            for (Method method : methods) {
                String methodName = method.getName();
                if ((methodName.startsWith("get") || methodName.startsWith("is")) && method.getParameterTypes().length == 0) {
                    method.setAccessible(true);
                    Class<?> vClass = method.getReturnType();
                    Object value = method.invoke(src);
                    if (value != null) {
                        // 拼接设置值的方法名
                        String setValMethodName = methodName.replaceFirst("(get)|(is)", "set");
                        try {
                            Method destSetValClassMethod = null;
                            try {
                                destSetValClassMethod = destClass.getMethod(setValMethodName, vClass);
                            } catch (NoSuchMethodException e) {
                                if (CommonUtils.PACKING_BASIC_TYPES.containsKey(vClass)) {
                                    destSetValClassMethod = destClass.getMethod(setValMethodName, CommonUtils.PACKING_BASIC_TYPES.get(vClass));
                                } else if (CommonUtils.BASIC_PACKING_TYPES.containsKey(vClass)) {
                                    destSetValClassMethod = destClass.getMethod(setValMethodName, CommonUtils.BASIC_PACKING_TYPES.get(vClass));
                                } else {
                                    throw e;
                                }
                            }
                            destSetValClassMethod.invoke(dest, value);
                        } catch (NoSuchMethodException e) {
                            //
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            if (!errContinue) {
                                throw e;
                            }
                        }
                    }
                }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}